import { Provide, Inject } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
import { CaptchaService } from '@midwayjs/captcha';
import { JwtService } from "@midwayjs/jwt"; // 鉴权和校验
import { LoginDTO } from "@dto/login.dto"; // 登录参数格式
import { InjectEntityModel } from "@midwayjs/typeorm";
import { Repository } from "typeorm";
import { UserEntity } from "@entity/system/user.entity";
import { LoginInforEntity } from "@entity/monitor/loginInfor.entity";
import { isEqualPsw } from '@/utils/password';
import { resBuild } from "@utils/resBuild";
import { formatDate } from '@utils/time'
import { getIp } from '@/utils/device'
import * as useragent from 'useragent'
import { HttpService } from '@midwayjs/axios';
import { RedisService } from '@midwayjs/redis';
import { RedisEnum } from "@utils/enum";
import { Decrypt } from '@/utils/aes';

@Provide()
export class LoginService {
  @Inject()
  ctx: Context;

  @Inject()
  captchaService: CaptchaService;

  @Inject()
  jwtService: JwtService;

  @Inject()
  httpService: HttpService;

  @Inject()
  redisService: RedisService;

  @InjectEntityModel(UserEntity)
  userEntity: Repository<UserEntity>;

  @InjectEntityModel(LoginInforEntity)
  loginInforEntity: Repository<LoginInforEntity>;

  // 生成验证码
  async captchaImage() {
    const temp = await this.redisService.get(`${RedisEnum.SYS_CONFIG_KEY}sys.account.captchaEnabled`);
    const isCaptchaEnabled = temp === null ? true : JSON.parse(temp)
    if(JSON.parse(isCaptchaEnabled)) {
      // 如果开启了验证码，则生成验证码
      const { id, imageBase64 } = await this.captchaService.formula({ noise: 1 }); // noise是干扰项条数，具体配置可看文档
      // 把校验id存起来，和后面的登录时的参数、对比值是否一致
      this.ctx.session.codeId = id;
      return {
        code: 200,
        msg: '操作成功',
        img: imageBase64, // 此处直接返回base64图片，和若依的base64位字符串不一样
        captchaEnabled: isCaptchaEnabled, // 是否开启验证码校验
      }
    } else {
      // 如果关闭了验证码，则返回captchaEnabled值为false
      return resBuild.more({
        captchaEnabled: isCaptchaEnabled, // 是否开启验证码校验
      })
    }
  }

  // 登录接口
  async login(body: LoginDTO) {
    const { username, password, code } = body;

    // 先查询用户表是否有这个用户名
    const userInfoTemp: UserEntity = await this.userEntity.findOne({
      where: {
        userName: username
      }
    })

    // 如果缓存获取为空，默认开启，否则按缓存走
    const temp = await this.redisService.get(`${RedisEnum.SYS_CONFIG_KEY}sys.loginLog.enable`)
    const isUseLoginLog = temp === null ? true : JSON.parse(temp)

    if(isUseLoginLog) {
      setTimeout(async () => {
        await this.recordLog(username);
      }, 100)
    }

    // 如果用户存在，进行下一步，否则提示不存在用户
    if(userInfoTemp) {
      // 如果开启了验证码，验证码是否相等
      if(this.ctx.session.isCaptchaEnabled) {
        const isEqualCode: boolean = await this.captchaService.check(this.ctx.session.codeId, code);
        if(!isEqualCode) {
          throw new Error('验证码错误')
        }
      }

      // 密码是否相等
      const isEqualPswFlag: boolean = await isEqualPsw(password, userInfoTemp.password.trim());
      if(!isEqualPswFlag) {
        throw new Error('账号或密码错误')
      }
    } else {
      throw new Error('用户不存在')
    }

    // 通过校验后，设置本地缓存、返回token
    this.ctx.session.userInfo = userInfoTemp;
    const token: string = await this.jwtService.sign({
      userId: userInfoTemp.userId,
      username: userInfoTemp.userName,
    })
    // 设置token相关配置，更多配置请自行查阅，这个一般够用了
    this.ctx.cookies.set('token', token, {
      maxAge: 24 * 60 * 60 * 1000, // 有效期为一天
      httpOnly: true, // 只能通过 HTTP 请求访问，不能通过 JavaScript 访问
      signed: true, // 签名 cookie，防止篡改
      overwrite: true, // 如果已有同名 cookie，则覆盖
    })

    return resBuild.more({
      token: token
    })
  }

  /** 退出登录 */
  async logout() {
    this.ctx.cookies.set('token', '', {
      maxAge: 0, // 0表示立即过期
      httpOnly: true, // 只能通过 HTTP 请求访问，不能通过 JavaScript 访问
      signed: true, // 签名 cookie，防止篡改
      overwrite: true, // 如果已有同名 cookie，则覆盖
    })
    this.ctx.session.userInfo = null;
    return resBuild.success();
  }

  // 记录登录日志
  async recordLog(username: string) {

    const webAgent = this.ctx.request.header['user-agent']

    const temp = await this.redisService.get(`${RedisEnum.SYS_CONFIG_KEY}sys.interface.enable`)
    const isOpenCrypt = temp === null ? true : JSON.parse(temp)

    const body: any = isOpenCrypt ? JSON.parse((Decrypt(this.ctx.response.body))) : this.ctx.response.body
    const ip = getIp(this.ctx)
    let loginLocation: string = ''

    // 如果拆分后长度为4，则是ipv4地址，且不带::本地
    if(ip && !ip.includes('::') && ip.split('.').length === 4) {
      // 如果拆分后长度为4，则是ipv4地址，且不带::本地
      if(ip && !ip.includes('::') && ip.split('.').length === 4) {
        const tempIpInfo = await this.httpService.get(`https://opendata.baidu.com/api.php?query=${ip}&co=&resource_id=6006&oe=utf8`);
        loginLocation = `${tempIpInfo.data.data[0].location}`.replace('省', ' ').replace('市', '')
      }
    }

    if(webAgent) {
      const myAgent = useragent.parse(this.ctx.request.header['user-agent']);
      const os = myAgent.os.toJSON().family;
      const browser = myAgent.toAgent();

      // 登录时记录登录日志
      const logData = {
        userName: username,
        ipaddr: ip,
        loginLocation,
        browser,
        os,
        status: body?.code === 200 ? '0' : '1',
        msg: body?.msg || '',
        loginTime: formatDate(new Date())
      }

      const tempEntity: any = this.loginInforEntity.create(logData);
      await this.loginInforEntity.save(tempEntity);
    } else {
      // 登录时记录登录日志
      const logData = {
        userName: username,
        ipaddr: ip,
        loginLocation,
        browser: null,
        os: null,
        status: body?.code === 200 ? '0' : '1',
        msg: body?.msg || '',
        loginTime: formatDate(new Date())
      }

      const tempEntity: any = this.loginInforEntity.create(logData);
      await this.loginInforEntity.save(tempEntity);
    }
  }
}
